#!/usr/bin/env python
# -*- coding:utf-8 -*-


"""
类: 抽象的模板. 
实例: 根据类创建出来的一个个具体的"对象".
(object) 表示该类是从哪个类继承下来的.
如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类.
在创建实例时, 通过定义一个特殊的__init__方法,来绑定属性.

类是创建实例的模板.
实例是一个一个具体的对象.
各个实例拥有的数据互相独立,互不影响.

方法是与实例绑定的函数, 方法可以直接访问实例的数据.

通过在实例上调用方法,就可以直接擦偶作对象内部的数据.
Python允许对实例变量绑定任何数据.

注意:
    __init__方法的第一个参数是self,表示创建的实例本身.
    在__init__方法内部,就可以把各种属性绑定到self,self就指向创建的实例本身.

在类中定义的函数, 第一个参数必须是实例变量self. 调用时,不用传递该参数.

封装的另一个优点是可以给Student类增加新的方法. get_grade。
"""
"""
class Student(object):

    def __init__(self,name,score):
        self.name = name 
        self.score = score 

    # def print_score(self):
        # print("{}\t{}".format(self.name,self.score))

    def get_grade(self):

        if self.score >= 90:
            return 'A'
        elif self.score >= 60:
            return 'B'
        else:
            return 'C'
	
# test = Student('testclass',30)
# print(test.name)
# print(test.score)
# print(test.print_score())

# get_grade方法可以直接在实例变量上调用.
test = Student('testclass',59)
print(test.get_grade())
"""



"""
访问限制:
    在类内部,可以有属性和方法,外部代码可以通过直接调用实例变量的方法来操作数据.
    让内部属性不被外部访问,可以把属性的名称前加上两个下划线.
    私有变量以__开头,只有内部可以访问,外部不能访问.

    以双下划线开头,并且以双下划线结尾,是特殊变量,特殊变量是可以直接访问的.
    以一个下划线开头的实例变量名,这个实例变量外部可以访问.但是不建议访问.
"""
"""
class Student(object):

    def __init__(self,name,score):
        self.__name = name 
        self.__score = score
    
    # 无法从外部访问实例变量
    def print_score(self):
        print("{}: {}".format(self.__name,self.__score))



test = Student('testclass',98)
# 报错内容为:AttributeError: 'Student' object has no attribute '__name'
print(test.__name)
# 外部代码不能随意修改对象内部的状态. 
"""



# 让外部代码获取name和score
"""
class Student(object):

    def __init__(self,name,score):
        self.__name = name 
        self.__score = score
    
    # 无法从外部访问实例变量
    def print_score(self):
        print("{}: {}".format(self.__name,self.__score))
    
    # 让外部代码获取name
    def get_name(self):
        return self.__name
    
    # 让外部代码获取score
    def get_score(self):
        return self.__score
    
    # 允许外部代码修改score
    def set_score(self,score):
        # self.__score = score
        if 0 <= score <= 100:
            self.__score = score 
        else:
            raise ValueError('bad score')
"""





# 继承和多态
"""
定义一个class时,可从某个现有的class继承,新的class称为子类.
被继承的class称为基类、父类或超类.

判断一个变量是否是某个类型可以用isinstance()判断.
"""
"""
class Animal(object):
    def run(self):
        print('Animal is running...')

# 编写Dog和Cat类时,可从Animal类继承
class Dog(Animal):
    
    def run(self):
        print('Dog is runnning...')

    def eat(self):
        print('Eating some food')

class Cat(Animal):
    
    def run(self):
        print('Cat is running ...')
    
    def eat(self):
        print('Eating some fish')

# 对于Dog来说,Animal是它的父类,对于Animal来说,Dog就是它的子类. Dog和Cat默认拥有run方法.
dog = Dog()
dog.run()
dog.eat()

cat = Cat()
cat.run()
cat.eat()
"""
"""
说明:
    当子类和父类存在相同run()方法时,子类的run()覆盖了父类的run()。
    代码运行时,会调用子类的run().
"""


# 理解多态
"""
def run_twice(animal):
    animal.run()
    animal.run()

# 传入Animal实例时:
run_twice(Animal())

# 传入Dog的实例时:
run_twice(Dog())

# 传入Cat实例时:
run_twice(Cat())

class Tortoise(Animal):
    def run(self):
        print('Tortoise is running slowly...')

# 调用run_twice时,传入Tortoise实例:
run_twice(Tortoise())
"""
"""
开闭原则:
对扩展开放: 允许新增Animal子类.
对修改封闭: 不需要修改依赖Animal类型的run_twice()等函数.

用type()函数来判断对象类型.
如果一个变量指向函数或者类,可用type()判断.
"""

# 判断一个对象是否是函数? 使用types模块中定义的常量
"""
import types

def fn():
    pass

print(type(fn) == types.FunctionType)
print(type(abs) == types.BuiltinFunctionType)
print(type(lambda x: x) == types.LambdaType)
print(type((x for x in range(10))) == types.GeneratorType)
"""



"""
# 使用isinstance()
    判断class的类型,使用isinstance()函数
    isinstance()判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上.
    能用type()判断的基本类型也可以用isinstance()判断.

# 使用dir()
    获得一个对象的所有属性和方法.
    它返回一个包含字符串的list.
"""




# 使用getattr(),setattr()及hasattr()可直接操作一个对象的状态.
"""
class MyObject(object):
    def __init__(self):
        self.x = 9
    def power(self):
        return self.x * self.x 

obj = MyObject()
# 测试该对象的属性
print(hasattr(obj,'x'))
print(obj.x)
print(hasattr(obj,'y'))
print(setattr(obj,'y',19)) # 设置一个属性'y'
print(hasattr(obj,'y'))
print(getattr(obj,'y'))  # 获取属性'y'
print(obj.y)  # 获取属性'y'

# 获取不存在的属性,会抛出AttributeError错误.
# print(getattr(obj,'z'))
# AttributeError: 'MyObject' object has no attribute 'z'

# 设置返回默认值
print(getattr(obj,'z',404))  #属性不存在,返回默认值404

# 获得对象的方法
print(hasattr(obj,'power'))
print(getattr(obj,'power'))  # 获取属性'power'
fn = getattr(obj,'power') # 获取属性'power'并赋值到变量fn
print(fn)
print(fn()) # 这个和调用obj.power()是一样的。
"""





# 实例属性和类属性
"""
根据类创建的实例可以任意绑定属性.
给实例绑定属性的方法是通过实例变量,或者通过self变量.
禁止将实例属性和类属性使用相同的名字. 因为相同名称的实例属性将屏蔽掉类属性.
"""
"""
class Student(object):
    def __init__(self,name):
        self.name = name 

s = Student('Summar')
s.score = 90
print(s.score)
"""

# 在class中定义属性,这种属性时候类属性.
class Student(object):
    name = 'Student'

s = Student() # 创建实例s
print(s.name) # 打印name属性.
print(Student.name) # 打印类的name属性.
s.name = 'Michael' # 给实例绑定name属性.
print(s.name) # 实例属性优先级比类属性高.
print(Student.name) # 类属性没有消失,用Student.name可以访问.





